package utils;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Observable;
import java.util.Vector;

import nanoxml.XMLElement;
import client.GameSettings;

import commands.Commands;
import enums.PlayerColor;


/**
 * A list of all the tables.
 */
public class TableList extends Observable implements ITransmittable{

	private HashMap<Integer, Table> tableList;
	private Vector<Integer> unusedTableNumbers;

	private int highestTableNum;

	
	public TableList () {
	    // Create new hash map for tables
		tableList = new HashMap<Integer, Table> ();
		unusedTableNumbers = new Vector<Integer>();
		highestTableNum = 0;
	}
	
	@SuppressWarnings("unchecked")
	public TableList (XMLElement element) {
	    this ();
		Enumeration<XMLElement> e = element.enumerateChildren();
		while (e.hasMoreElements()) {
			XMLElement childElement = e.nextElement();
			if (childElement.getName().equals(Commands.TABLE)) {
				Table table = new Table(childElement);
				tableList.put (new Integer(table.getTableNum()), table);	
				highestTableNum = Math.max(highestTableNum, table.getTableNum());
			}
		}
	
		// Fill in the unusedTableNumbers vector with tables that are less than
		// highestTablenum, but not in the table.
		for (int i = 1; i < highestTableNum; i++) {
			Integer keyI = new Integer(i);
			if (!tableList.containsKey(keyI)) {
				unusedTableNumbers.add(keyI);
			}
		}
	}
	
	
	public XMLElement toXML() {
		XMLElement message = new XMLElement();
		message.setName(Commands.TABLE_LIST);
		int [] tableNums = getTablesNumbers();
		for (int i = 0; i < tableNums.length; i++) {
			Table table = getTable(tableNums[i]);
			message.addChild (table.toXML());
		}
		return message;
	}
	
	public Table addTable (User creator, String ownerIP , PlayerColor color, GameSettings gameSettings) {
	    // Generate new table number.
		Integer newTableNum = computeNewTableNum();
		
		// Create new Table object.
		Table table = new Table (newTableNum.intValue(), creator , ownerIP, color, gameSettings);
		
		// Store in HashMap using the table number as its key.
		tableList.put (newTableNum, table);
		
	    // Notify listeners
	    setChanged ();
		notifyObservers ("+T " + newTableNum);

		// Return the table number
		return table;
	}

	public void removeTable (int tableNum) {
		Integer iTableNum = new Integer (tableNum);
		tableList.remove (iTableNum);

		// Reclaim this table number for reuse.
		if (tableNum == highestTableNum) {
			highestTableNum -= 1;
		} else {
			unusedTableNumbers.add(iTableNum);
		}

	    // Notify listeners
	    setChanged ();
		notifyObservers ("-T " + tableNum);
	}

	public boolean removePlayer (int tableNum, String player) {
		Table table = getTable (tableNum);

		// Verify that table & player exist
		if ((table == null) || (player == null)) {
			return false;
		}

		// Verify that the player is removed from the player list.
		PlayerList players = table.getPlayerList();
		if (!players.removePlayer (player)) {
			return false;
		}

		// See if the table is now empty, and if so, remove it.
		if (players.size() == 0) {
			removeTable (tableNum);
		}

		// Notify listeners
		setChanged ();
		notifyObservers ("-P " + tableNum + " " + player);

		return true;
	}

	public void removeUserFromTables (String player) {
		Vector<Integer> keys = new Vector<Integer> (tableList.keySet());

		// Loop through each table and remove the player
		for (int i = 0; i < keys.size(); i++) {
			int tableNum = ((Integer)keys.get(i)).intValue();
			removePlayer (tableNum, player);
		}

	    // Notify listeners
	    setChanged ();
		notifyObservers ("-U " + player);
	}

	public Table getTable (int tableNum) {
		return (Table)tableList.get(new Integer(tableNum));
	}

	public int [] getTablesNumbers () {
		Vector<Integer> keys = new Vector<Integer> (tableList.keySet());
		Collections.sort(keys);

		// Create integer and store key values it
		int [] intKeys = new int [tableList.size()];
		for (int i = 0; i < keys.size(); i++) {
			int keyNum = ((Integer)keys.get(i)).intValue();
			intKeys [i] = keyNum;
		}

		return intKeys;
	}

	public void updateTable (int tableNum, Table table) {
		int tableCount = tableList.size();
		tableList.put (new Integer(tableNum), table);

		String notify = "";
		if (tableList.size() > tableCount)
			notify = "+T " + tableNum;

		// Notify listeners
	    setChanged ();
		notifyObservers (notify);
	}

	/**
	 * Return the size of the table list.
	 *
	 * @return   Number of Table objects in TableList.
	 */
	public int size () {
		return tableList.size();
	}

	/**
	 * Return a list of table numbers of a user.
	 *
	 * @param username - The username for which to check the table numbers.
	 * 
	 * @return The list of table numbers of the given user.
	 */
	public int [] getTableNumsForUser (String username) {

		int [] tableNums = getTablesNumbers();
		int [] userTableNumsTemp = new int [tableNums.length];

		int c = 0;
		for (int i = 0; i < tableNums.length; i++) {
			Table table = getTable(tableNums[i]);
			if (table.containsPlayer(username))
				userTableNumsTemp [c++] = tableNums[i];
		}

		// Create new integer array with populated values
		int [] userTableNums = new int [c];
		for (int i = 0; i < c; i++)
			userTableNums [i] = userTableNumsTemp [i];
		return userTableNums;
	}

	/**
	 * Return the number of tables that a specified user has created
	 * i.e. that the user is the owner of.
	 *
	 * @param username - Username to check.
	 * @return           Number of tables this user is owner of.
	 */
	public int getNumOfTablesUserOwns (String username) {
		int [] tableNums = getTablesNumbers();

		int count = 0;
		for (int i = 0; i < tableNums.length; i++) {
			Table table = getTable (tableNums[i]);
			if (table.getOwnerUsername().equals (username))
				count++;
		}

		return count;
	}

	private Integer computeNewTableNum () {
		if (unusedTableNumbers.isEmpty()) {
			// No table numbers are available to recycle, so all tables from
			// 1 to highestTableNum are being used.  Therefore, our new table
			// number is (highestTableNum + 1)
			highestTableNum += 1;
			return new Integer (highestTableNum);
		} else {
			// Pull the first number out of unusedTableNums and recycle it.
			return unusedTableNumbers.remove(0);
		}
	}

	public void refreshObservers () {
		setChanged();
		notifyObservers();	 // notify any class which observes this class
	}

	public Table getTableByOwner(String usernameOwner)
	{
		Table tableToReturn = null;
		
		for (Table currTable : tableList.values())
		{
			if (currTable.getOwnerUsername().equals(usernameOwner))
			{
				tableToReturn = currTable;
				
				break;
			}
		}
		
		return tableToReturn;
	}
}
